package org.zoomdev.zoom.event.modules;


import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zoomdev.zoom.aop.Invoker;
import org.zoomdev.zoom.aop.MethodInvokerFactory;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.utils.CachedClasses;
import org.zoomdev.zoom.common.utils.Classes;
import org.zoomdev.zoom.event.EventListener;
import org.zoomdev.zoom.event.EventService;
import org.zoomdev.zoom.event.annotations.EventObserver;
import org.zoomdev.zoom.ioc.Ioc;
import org.zoomdev.zoom.security.Sensitives;

import java.lang.reflect.Method;

public class EventObserverInterceptorFactory {

    private static final Logger log = LoggerFactory.getLogger(CoreEventModule.NAME);
    private final EventService eventService;
    private final Ioc ioc;

    public EventObserverInterceptorFactory(EventService eventService, Ioc ioc) {
        this.eventService = eventService;
        this.ioc = ioc;
    }

    static class InnerMethodInvoker implements EventListener {
        private final Class targetClass;
        private final Invoker method;
        private final String methodDesc;
        private final Ioc ioc;
        private final String event;
        private Object target;

        public InnerMethodInvoker(Ioc ioc, Class targetClass, Method method, String methodDesc, String event) {
            this.targetClass = targetClass;
            this.ioc = ioc;
            this.method = MethodInvokerFactory.FACTORY.create(method);
            this.methodDesc = methodDesc;
            this.event = event;
        }


        @Override
        public void onEvent(Object data) {
            try {
                if (target == null) {
                    target = ioc.fetch(targetClass);
                }
                if (CoreEventModule.enableLog) {
                    log.info("通知[" + event + "] ==> " + methodDesc + " DATA: " + Sensitives.toSensitiveString(data));
                }
                method.invoke(target, new Object[]{data});
            } catch (Throwable e) {
                if (CoreEventModule.enableLog) {
                    log.error("通知[" + event + "] ==> " + methodDesc + " 异常", e);
                }
                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                }
                throw new ZoomException(e);
            }
        }
    }

    public void register(Class<?> modelClass) {
        try {
            Method[] methods = CachedClasses.getPublicMethods(modelClass);
            for (Method method : methods) {
                EventObserver eventObserver = method.getAnnotation(EventObserver.class);
                if (eventObserver == null) {
                    continue;
                }
                String eventName = eventObserver.name();
                if (StringUtils.isEmpty(eventName)) {
                    throw new ZoomException("观察者必须给出需要观察的事件名称" + method);
                }
                createListener(eventName, modelClass, method);
            }
        } catch (NoClassDefFoundError e) {
            log.warn("class:" + modelClass + " 被忽略,找不到类:" + e.getMessage());
        }
    }

    public void createListener(String eventName, Class<?> targetClass, Method method) {
        String methodDesc = Classes.getMethodDesc(targetClass, method);
        if (CoreEventModule.enableLog) {
            log.info("注册监听器: " + eventName + " ==> " + methodDesc);
        }
        EventListener listener = new InnerMethodInvoker(ioc, targetClass, method, methodDesc, eventName);
        eventService.addListener(eventName, listener);
    }
}